package Connections;

import Gear.Factory;

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.util.ArrayList;

/**
 * Class with a singleton peer and its methods.
 *
 * @author Rodrigo Martins Racanicci
 */
public class Peer {

    /**
     * Peer instance.
     */
    private static Peer instance = null;

    /**
     * Peer client.
     */
    private Client peer;

    /**
     * Peer
     */
    private String command;

    /**
     * Peer constructor.
     */
    private Peer() {
        peer = new Client();
        command = "";
    }

    /**
     * Get a peer instance.
     *
     * @return Peer instance.
     */
    public static Peer getInstance() {
        if (instance == null) {
            instance = new Peer();
        }
        return instance;
    }

    /**
     * Build command to inform that it is alive.
     *
     * @return Peer instance.
     */
    public Peer iAmAlive() {
        command = command.concat("IAA;");

        return this;

    }

    /**
     * List the online peers on network.
     */
    public void lsPeers() {

        peer.sendCommand("LsPeers");

        String peers = peer.receiveCommand();

        System.out.println("<PEERS>");
        for (String p : peers.split(" ")) {
            System.out.println(p);
        }

        peer.close();
        Peer.instance = null;
    }

    /**
     * List the available files on network.
     */
    public void lsX() {

        @SuppressWarnings ("MismatchedReadAndWriteOfArray") File[] files = Factory.getInstance().getSettings().getGearPath().toFile().listFiles();
        ArrayList<String> localFiles = new ArrayList();

        for (File file : files) {
            localFiles.add(file.getName());
        }

        peer.sendCommand("LsX");
        String names = peer.receiveCommand();

        System.out.println("<FILE NAME>");
        for (String s : names.trim().split("\"{1,2}")) {
            if (!localFiles.contains(s.trim())) {
                System.out.println(s);
            }
        }

        peer.close();
        Peer.instance = null;
    }

    /**
     * Build command to inform that it is not alive anymore.
     *
     * @return Peer instance.
     */
    public Peer iAmNotAlive() {

        command = command.concat("IANA;");
        return this;
    }

    /**
     * Verify if there is inconsistent files and delete them.
     */
    public void syncronizeFiles() {
        peer.sendCommand("LsX");
        String names = peer.receiveCommand();

        File gearDirectory = Factory.getInstance().getSettings().getGearPath().toFile();

        @SuppressWarnings ("MismatchedReadAndWriteOfArray") File[] files = gearDirectory.listFiles();

        for (File f : files) {
            if (names.contains("\"" + f.getName() + "\"")) {
                try {
                    f.setWritable(true);
                    Files.delete(FileSystems.getDefault().getPath(Factory.getInstance().getSettings().getGearPath().toString(), f.getName()));
                    System.out.println("There's already a file " + f.getName() + "in the network, your file was deleted... redownload it if you wish!");
                } catch (IOException ex) {
                    System.out.println("Error on deleting operation.");
                }
            }
        }
        peer.close();
        Peer.instance = null;
    }

    /**
     * Syncronize local files with the tracker.
     *
     * @return Peer instance.
     */
    public Peer sycronizeSharedFolder() {
        File gearDirectory = Factory.getInstance().getSettings().getGearPath().toFile();

        @SuppressWarnings ("MismatchedReadAndWriteOfArray") File[] files = gearDirectory.listFiles();

        command = command.concat("RegisterFile ");
        for (File f : files) {
            f.setReadOnly();
            command = command.concat("\"" + f.getName() + "\"");
        }
        command = command.concat(";");

        return this;
    }

    /**
     * Send the commands to tracker.
     */
    public void send() {
        command = command.concat("Close");
        peer.sendCommand(command);
        peer.close();
        Peer.instance = null;
    }

    /**
     * Check if a file is available in network.
     *
     * @param fileName File name.
     *
     * @return true means available, false otherwise
     */
    public boolean checkFile(String fileName) {
        peer.sendCommand("LsX");

        boolean check = peer.receiveCommand().contains("\"" + fileName + "\"");

        peer.close();
        Peer.instance = null;

        return check;
    }

    /**
     * Try to create a new file on tracker.
     *
     * @param fileName File name.
     *
     * @return true means success, false otherwise.
     */
    public boolean newFile(String fileName) {
        peer.sendCommand("NewFile \"" + fileName + "\"");

        boolean check = Boolean.parseBoolean(peer.receiveCommand());

        peer.close();
        Peer.instance = null;

        return check;
    }

    /**
     * Try to remove a file of the tracker.
     *
     * @param fileName File Name.
     *
     * @return true means success, false otherwise.
     */
    public boolean removeFile(String fileName) {
        peer.sendCommand("RemoveFile \"" + fileName + "\"");
        boolean response = Boolean.parseBoolean(peer.receiveCommand());
        peer.close();
        Peer.instance = null;

        return response;
    }

    /**
     * Request a peer which contains the specified file.
     *
     * @param fileName Required file name.
     *
     * @return Peer host or null if the file is not available.
     */
    public String requestPeer(String fileName) {
        peer.sendCommand("RequestPeer \"" + fileName + "\"");
        String response = peer.receiveCommand();

        peer.close();
        Peer.instance = null;

        return response;
    }

    /**
     * Try to acquire a lock of a file.
     *
     * @param fileName File name to lock.
     *
     * @return true means success, false otherwise.
     */
    public boolean lockFile(String fileName) {
        peer.sendCommand("LockFile \"" + fileName + "\"");

        boolean response = Boolean.parseBoolean(peer.receiveCommand());

        peer.close();
        Peer.instance = null;

        return response;

    }

    /**
     * Try to release a lock of a file.
     *
     * @param fileName File name to unlock.
     *
     * @return true means success, false otherwise.
     */
    public boolean unlockFile(String fileName) {
        peer.sendCommand("UnlockFile \"" + fileName + "\"");

        boolean response = Boolean.parseBoolean(peer.receiveCommand());

        peer.close();
        Peer.instance = null;

        return response;

    }
}
